﻿using System.IO.Ports;
using System.Net;
using System.Net.Sockets;
using System.Threading.Channels;
using TrackSystem.Entities.Dtos.Channel;
using TrackSystem.Entities.Extensions;
using TrackSystem.Entities.Models;
using TrackSystem.IO.Alarm;

namespace TrackSystem.IO.Ports
{
    public class PortBase : IPortBase
    {
        #region  共用成员变量定义
        /// <summary>
        /// 最后一次接收通信帧
        /// </summary>
        public byte[] RxBuf { get; protected set; }

        /// <summary>
        /// 最后一次发送通信帧
        /// </summary>
        public byte[] TxBuf { get; protected set; }

        /// <summary>
        /// 端口种类
        /// </summary>
        public PortCategory type { get; protected set; }
        #endregion

        #region 私有变量定义
        private string Name;
        private IPEndPoint remotePoint;
        private EndPoint remotePt;
        private IPEndPoint localEndPoint;
        private Socket socket;
        private SerialPort com;
        private string _portName;
        private int _baudrate;
        private string? _serialParams;
        #endregion

        #region 构造函数
#pragma warning disable CS8618 
        public PortBase()
#pragma warning restore CS8618 
        {
            RxBuf = [];
            TxBuf = [];
            Connected = false;
        }

        #endregion
        public bool Connected { get; protected set; }

        /// <summary>
        /// 初始化端口
        /// </summary>
        /// <param name="channel">通道参数</param>
        /// <returns></returns>
        public bool Initialize(ChannelTable channel)
        {
            Name = channel.Name;
            type = channel.PortCategory;
            if (type == PortCategory.TCP)
            {
                remotePoint = new IPEndPoint(IPAddress.Parse(channel.Ip??="127.0.0.1"), channel.Port);
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            }
            else if (type == PortCategory.UDP)
            {
                remotePoint = new IPEndPoint(IPAddress.Parse(channel.Ip ??= "127.0.0.1"), channel.Port);
                localEndPoint = new IPEndPoint(IPAddress.Parse(channel.localIp ??= "127.0.0.1"), channel.localPort);
                remotePt = new IPEndPoint(IPAddress.Any, 0);
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            }
            else
            {
                _portName = channel.COMn ??= "N-8-1";
                _baudrate = channel.Baudrate;
                _serialParams = channel.SerialParams;
                com = InitSerialPort(_portName, _baudrate, _serialParams);
            }

            return true;
        }

        /// <summary>
        /// 初始化参数
        /// </summary>
        /// <param name="portName"></param>
        /// <param name="baudRate"></param>
        /// <param name="serialParams"></param>
        /// <returns></returns>
        private SerialPort InitSerialPort(string portName, int baudRate, string? serialParams)
        {
            var pt = new SerialPort();
            pt.PortName = portName;
            pt.BaudRate = baudRate;
            var comParams = (serialParams ?? "N-8-1").ToUpper().Trim().Split('-');  // 将列入“N-8-1”分开
            if (comParams.Length < 3) return pt;

            pt.Parity = comParams[0] switch
            {
                "N" => Parity.None,
                "E" => Parity.Even,
                "O" => Parity.Odd,
                _ => pt.Parity
            };

            pt.DataBits = comParams[1] switch
            {
                "8" => 8,
                "7" => 7,
                _ => pt.DataBits
            };

            pt.StopBits = comParams[2] switch
            {
                "1" => StopBits.One,
                "2" => StopBits.Two,
                "1.5" => StopBits.OnePointFive,
                _ => pt.StopBits
            };
            return pt;
        }

        /// <summary>
        /// 打开端口
        /// </summary>
        /// <returns></returns>
        public bool Open()
        {
            try
            {
                if (Connected)
                {
                    Close();
                }

                switch (type)
                {
                    case PortCategory.TCP:
                    {
                        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        socket.ReceiveTimeout = 100;
                        socket.SendTimeout = 100;
                        socket.ReceiveBufferSize = 1024;
                        socket.SendBufferSize = 1024;

                        Thread.Sleep(new Random(new Guid().GetHashCode()).Next(100));
                        var connResult = socket.BeginConnect(remotePoint, null, null);
                        connResult.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(2));
                        if (!connResult.IsCompleted)
                        {
                            Connected = false;
                            socket.Close();
                            return false;
                        }

                        break;
                    }
                    case PortCategory.UDP:
                        socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                        socket.ReceiveTimeout = 100;
                        socket.SendTimeout = 100;
                        socket.Bind(localEndPoint);
                        break;
                    
                    default:
                        // com = InitSerialPort(_portName, _baudrate, _serialParams);
                        com.Open();
                        com.ReadTimeout = 100;
                        com.WriteTimeout = 100;
                        break;
                }

                Connected = true;
                return true;
            }
            catch (Exception ex)
            {
                Connected = false;
                AlarmFactory.Instance.Insert($"端口打开故障, {ex.Message}", AlarmCategory.COMM, Name, 100);
                return false;
            }
        }

        /// <summary>
        /// 关闭端口
        /// </summary>
        /// <returns></returns>
        public bool Close()
        {
            if (type == PortCategory.TCP || type == PortCategory.UDP)
            {
                socket.Close();
            }
            else
            {
                com.Close();
            }
            Connected = false;
            return true;
        }

        /// <summary>
        /// 从端口读取字节
        /// </summary>
        /// <param name="buffer">读取的字节缓存</param>
        /// <returns>返回实际读取到的字节数量</returns>
        public int Read(byte[] buffer)
        {
            switch (type)
            {
                case PortCategory.TCP:
                    try
                    {
                        var nums = socket.Receive(buffer);
                        RxBuf = buffer;
                        return nums;
                    }
                    catch (Exception)
                    {
                        if (socket.Poll(10, SelectMode.SelectRead))
                        {
                            return 0;
                        }
                        throw;
                    }
                case PortCategory.UDP:
                    try
                    {
                        var nums = socket.ReceiveFrom(buffer, ref remotePt);
                        RxBuf = buffer;
                        return nums;
                    }
                    catch (Exception)
                    {
                        return 0;
                    }
                case PortCategory.SRIAL:
                default:
                {
                    var bytes = com.BytesToRead;
                    if (bytes > 1024)
                    {
                        bytes = 1024;
                    }
                    var nums = com.Read(buffer, 0, bytes);
                    RxBuf = buffer;
                    return nums;
                }
            }
        }

        /// <summary>
        /// 向端口写入字节
        /// </summary>
        /// <param name="buffer">写入的字节缓冲</param>
        /// <returns>返回实际写入的字节数</returns>
        public int Write(byte[] buffer)
        {
            TxBuf = buffer;
            switch (type)
            {
                case PortCategory.TCP:
                    return socket.Send(buffer);
                case PortCategory.UDP:
                    return socket.SendTo(buffer, remotePoint);
                case PortCategory.SRIAL:
                default:
                    com.Write(buffer, 0, buffer.Length);
                    return buffer.Length;
            }
        }

        public bool NeedReset()
        {
            var result = type == PortCategory.TCP; //  && socket.Poll(10, SelectMode.SelectRead));
            return result;
        }
    }
}
